package com.example.filemanager.ext

import android.text.format.Formatter
import android.util.Log
import com.amaze.filemanager.fileoperations.filesystem.OpenMode
import com.example.filemanager.HybridFileParcelable
import com.example.filemanager.command.ListFilesCommand
import com.example.filemanager.data.LayoutElementParcelable
import com.example.filemanager.emun.FileType
import com.example.filemanager.utils.Applications
import com.example.filemanager.utils.DataUtils
import java.io.File
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.math.min

/**
 * Author:libowu
 * Date:2022/7/26
 * Describe: 对外方便直接操作文件的方法
 */
object FileManager {
    private val dataUtils: DataUtils = DataUtils.getInstance()
    //根目录文件夹
    private val mRootList: MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncRootList = Collections.synchronizedCollection(mRootList)
    //以某字符结尾的文件列表
    private val mFilesByEndWidthList: MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncFilesByEndWidthList = Collections.synchronizedCollection(mFilesByEndWidthList)
    //大文件列表
    private val mLageFileList: MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncLageFileList = Collections.synchronizedCollection(mLageFileList)
    //图片文件
    private val mImageFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncImageFileList = Collections.synchronizedCollection(mImageFileList)
    //音频文件
    private val mAutoFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncAutoFileList = Collections.synchronizedCollection(mAutoFileList)
    //安装包
    private val mApkFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncApkFileList = Collections.synchronizedCollection(mApkFileList)
    //文档文件
    private val mTextFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncTextFileList = Collections.synchronizedCollection(mTextFileList)
    //压缩文件
    private val mZipFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncZipFileList = Collections.synchronizedCollection(mZipFileList)
    //视频文件
    private val mVideoFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncVideoFileList = Collections.synchronizedCollection(mVideoFileList)
    //日志文件
    private val mLogFileList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncLogFileList = Collections.synchronizedCollection(mLogFileList)
    //空文件夹
    private val mEmptyDirectoryList:MutableList<LayoutElementParcelable> = mutableListOf()
    private val mSyncEmptyDirectoryList = Collections.synchronizedCollection(mEmptyDirectoryList)
    //默认地址
    private const val DEFAULT_PATH = "storage/emulated/0"
    //线程池
    private var mExecutorService: ExecutorService = Executors.newCachedThreadPool()
    //线程安全的map
    private var mConcurrentHashMap = ConcurrentHashMap<String, String>()
    //是否正在全盘扫描
    private var mIsScanning = false

    /**
     * 读取文件或文件夹以输入字符结尾的数据，回调在子线程，如果需要在回调重进下ui操作，无比切到ui进程再处理
     * @param path 目标路径
     * @param showHideFile 是否筛选隐藏文件
     * @param resultCallback 结果回调
     * @param progressCallback 过程回调
     */
    fun getFilesByEndWidth(
        endList: MutableList<String>,
        path: String = DEFAULT_PATH,
        showHideFile: Boolean = false,
        resultCallback: (filesList: MutableList<LayoutElementParcelable>, isCache: Boolean) -> Unit = { filesFile, isCache -> },
        progressCallback: (total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable -> }
    ) {
        if (mFilesByEndWidthList.isNotEmpty()) {
            resultCallback.invoke(mFilesByEndWidthList, true)
            return
        }
        val filesList = mutableListOf<LayoutElementParcelable>()
        val rootList = mutableListOf<LayoutElementParcelable>()
        val mSyncList = Collections.synchronizedCollection(filesList)
        var count = 0
        //如果线程池关闭，则重建线程池
        if (mExecutorService.isShutdown) {
            mExecutorService = Executors.newCachedThreadPool()
        }
        mExecutorService.execute {
            ListFilesCommand.listFiles(path, false, showHideFile,
                { mode ->
                    OpenMode.FILE
                    null
                }
            ) {
                val elem: LayoutElementParcelable? = createListParcelables(it);
                if (elem != null) {
                    if (elem!!.isDirectory) {
                        rootList.add(elem)
                    } else {
                        for (item in endList) {
                            if (elem?.desc.endsWith(item, true)) {
                                mSyncList.add(elem)
                            }
                        }
                    }
                }
            }
            for (elem in rootList) {
                if (!mExecutorService.isShutdown) {
                    mExecutorService.execute {
                        startFindContent(
                            elem?.desc,
                            mSyncList,
                            endList,
                            count,
                            progressCallback,
                            rootList.size
                        )
                        count += 1
                        if (count == rootList.size) {
                            resultCallback.invoke(filesList, false)
                            mFilesByEndWidthList.addAll(filesList)
                        }
                    }
                }
            }
            if (rootList.isEmpty()) {
                resultCallback.invoke(filesList, false)
            }
        }
    }

    private fun startFindContent(
        path: String,
        resultList: MutableCollection<LayoutElementParcelable>,
        endList: MutableList<String>,
        count: Int,
        progressCallback: (total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable) -> Unit,
        total: Int
    ) {
        ListFilesCommand.listFiles(path, false, true,
            { mode ->

            }
        ) {
            val elem: LayoutElementParcelable? = createListParcelables(it);
            if (elem != null) {
                if ((elem.isDirectory)) {
                    startFindContent(
                        elem?.desc,
                        resultList,
                        endList,
                        total,
                        progressCallback,
                        total
                    )
                } else if (!elem.isDirectory) {
                    for (item in endList) {
                        if (elem?.desc.endsWith(item, true)) {
                            //Log.i("日志", "log数据为：${elem?.desc}")
                            resultList.add(elem)
                            progressCallback.invoke(total, count, resultList.toMutableList(), elem)
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取大文件
     * @param path 扫描的文件地址
     * @param showHideFile 是否显示隐藏文件
     * @param minFileSize 大文件的最小大小(大文件阈值),单位是byte
     */
    fun getLargeFile(
        minFileSize: Long,
        showHideFile: Boolean = false,
        path: String = DEFAULT_PATH,
        resultCallback: (filesList: MutableList<LayoutElementParcelable>, isCache: Boolean) -> Unit = { filesFile, isCache -> },
        progressCallback: (total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable -> }
    ) {
        if (mLageFileList.isNotEmpty()) {
            resultCallback.invoke(mLageFileList, true)
            return
        }
        //如果线程池关闭，则重建线程池
        if (mExecutorService.isShutdown) {
            mExecutorService = Executors.newCachedThreadPool()
        }
        val filesList = mutableListOf<LayoutElementParcelable>()
        val mSyncList = Collections.synchronizedCollection(filesList)
        val rootList = mutableListOf<LayoutElementParcelable>()
        var count = 0
        mExecutorService.execute {
            ListFilesCommand.listFiles(path, false, showHideFile,
                { mode ->
                }
            ) {
                val elem: LayoutElementParcelable? = createListParcelables(it);
                if (elem != null && !elem.isDirectory && elem.longSize >= minFileSize) {
                    filesList.add(elem)
                } else if (elem != null && elem.isDirectory) {
                    rootList.add(elem)
                }
            }
            for (elem in rootList) {
                if (!mExecutorService.isShutdown) {
                    mExecutorService.execute {
                        getLargeFileChild(
                            mSyncList,
                            elem.desc,
                            showHideFile,
                            minFileSize,count,rootList.size,resultCallback,progressCallback
                        )
                        count += 1
                        //Log.i("日志","完成次数：${count}，总次数${rootList.size}")
                        if (count == rootList.size) {
                            resultCallback.invoke(filesList, false)
                            mLageFileList.addAll(filesList)
                        }
                    }
                }
            }
        }
    }

    private fun getLargeFileChild(
        mSyncList: MutableCollection<LayoutElementParcelable>,
        path: String,
        showHideFile: Boolean,
        minFileSize: Long,
        count:Int,
        total: Int,
        resultCallback: (filesList: MutableList<LayoutElementParcelable>, isCache: Boolean) -> Unit = { filesFile, isCache -> },
        progressCallback: (total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, resultList: MutableList<LayoutElementParcelable>, item: LayoutElementParcelable -> }
    ) {
        ListFilesCommand.listFiles(path, false, showHideFile,
            { mode ->
            }
        ) {
            val elem: LayoutElementParcelable? = createListParcelables(it);
            if (elem != null && !elem.isDirectory && elem.longSize >= minFileSize) {
                mSyncList.add(elem)
                progressCallback.invoke(total, count, mSyncList.toMutableList(), elem)
            } else if (elem != null && elem.isDirectory) {
                //目录
                getLargeFileChild(mSyncList, elem.desc, showHideFile, minFileSize,count,total,resultCallback,progressCallback)
            }
        }
    }

    fun getFilesByEndWidthCache(): MutableList<LayoutElementParcelable> {
        return mFilesByEndWidthList
    }

    /**
     * 扫描设备目录，获取分类
     */
    fun scanPhoneToGetAll(
        minFileSize: Long,
        showHideFile: Boolean = false,
        path: String = DEFAULT_PATH,
        resultCallback: (isCache: Boolean) -> Unit = {isCache -> },
        progressCallback: (total: Int, current: Int,item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, item: LayoutElementParcelable -> }
    ){
        mIsScanning = true
        if (mLageFileList.isNotEmpty()) {
            resultCallback.invoke(true)
            mIsScanning = false
            return
        }
        //如果线程池关闭，则重建线程池
        if (mExecutorService.isShutdown) {
            mExecutorService = Executors.newCachedThreadPool()
        }
        var count = 0
        if (mSyncRootList.isEmpty()){
            mExecutorService.execute {
                ListFilesCommand.listFiles(path, false, showHideFile,
                    { mode ->
                    }
                ) {
                    val elem: LayoutElementParcelable? = createListParcelables(it);
                    if (elem != null && !elem.isDirectory) {
                        addFileToList(elem, minFileSize,progressCallback,count)
                    } else if (elem != null && elem.isDirectory) {
                        var file = File(elem.desc)
                        if (file.listFiles().isEmpty()){
                            mSyncEmptyDirectoryList.add(elem)
                        }else{
                            mSyncRootList.add(elem)
                        }
                    }
                }
                for (elem in mRootList) {
                    if (!mExecutorService.isShutdown) {
                        mExecutorService.execute {
                            scanDirection(elem.desc,showHideFile,minFileSize,progressCallback,count)
                            count += 1
                            //Log.i("日志","完成次数：${count}，总次数${rootList.size}")
                            if (count == mRootList.size) {
                                resultCallback.invoke( false)
                            }
                        }
                    }
                }
            }
        }else{
            for (elem in mRootList) {
                if (!mExecutorService.isShutdown) {
                    mExecutorService.execute {
                        scanDirection(elem.desc,showHideFile,minFileSize,progressCallback,count)
                        count += 1
                        //Log.i("日志","完成次数：${count}，总次数${rootList.size}")
                        if (count == mRootList.size) {
                            resultCallback.invoke( false)
                            mIsScanning = false
                        }
                    }
                }
            }
        }
    }

    fun getFileList(type: FileType):MutableList<LayoutElementParcelable>{
        return when(type){
            FileType.APK -> mApkFileList
            FileType.COMPRESS -> mZipFileList
            FileType.LAGER_FILE -> mLageFileList
            FileType.IMAGE -> mImageFileList
            FileType.AUTO -> mAutoFileList
            FileType.VIDEO -> mVideoFileList
            FileType.TXT -> mTextFileList
            FileType.LOG -> mLogFileList
            FileType.EMPTY -> mEmptyDirectoryList
            else -> mRootList
        }
    }

    /**
     * 获取扫描状态
     * @return 是否正在扫描，true：正在扫描
     */
    fun isScanning():Boolean{
        return mIsScanning
    }

    private fun scanDirection(path:String,showHideFile: Boolean,minFileSize: Long, progressCallback: (total: Int, current: Int,item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, item: LayoutElementParcelable -> },count: Int){
        ListFilesCommand.listFiles(path, false, showHideFile,
            { mode ->
            }
        ) {
            val elem: LayoutElementParcelable? = createListParcelables(it);
            if (elem != null && !elem.isDirectory) {
                addFileToList(elem, minFileSize,progressCallback,count)
            } else if (elem != null && elem.isDirectory) {
                var file = File(elem.desc)
                if (file.listFiles().isEmpty()){
                    mSyncEmptyDirectoryList.add(elem)
                }else{
                    scanDirection(elem.desc,showHideFile,minFileSize,progressCallback,count)
                }
            }
        }
    }

    private fun addFileToList(elem: LayoutElementParcelable,minFileSize:Long, progressCallback: (total: Int, current: Int,item: LayoutElementParcelable) -> Unit = { total: Int, current: Int, item: LayoutElementParcelable -> },count:Int) {
        if (elem.longSize >= minFileSize){
            //大文件
            mSyncLageFileList.add(elem)
        }else if (elem.desc.endsWith(".apk",false)){
            //安装包
            mSyncApkFileList.add(elem)
        }else if (
            elem.desc.endsWith(".mp3",false) ||
            elem.desc.endsWith(".wav",false) ||
            elem.desc.endsWith(".ape",false) ||
            elem.desc.endsWith(".flac",false) ||
            elem.desc.endsWith(".aac",false) ||
            elem.desc.endsWith(".ogg",false) ||
            elem.desc.endsWith(".wma",false)
        ){
            //音频
            mSyncAutoFileList.add(elem)
        }else if (
            elem.desc.endsWith(".mp4",false) ||
            elem.desc.endsWith(".mov",false) ||
            elem.desc.endsWith(".wmv",false) ||
            elem.desc.endsWith(".fla",false) ||
            elem.desc.endsWith(".avi",false) ||
            elem.desc.endsWith(".avchd",false) ||
            elem.desc.endsWith(".webm",false) ||
            elem.desc.endsWith(".mkv",false)
        ){
            //视频
            mSyncVideoFileList.add(elem)
        }else if (
            elem.desc.endsWith(".bmp",false) ||
            elem.desc.endsWith(".jpg",false) ||
            elem.desc.endsWith(".jpeg",false) ||
            elem.desc.endsWith(".png",false) ||
            elem.desc.endsWith(".tif",false) ||
            elem.desc.endsWith(".gif",false) ||
            elem.desc.endsWith(".svg",false) ||
            elem.desc.endsWith(".psd",false) ||
            elem.desc.endsWith(".webp",false)
        ){
            //图片
            mSyncImageFileList.add(elem)
        }else if (
            elem.desc.endsWith(".log",false) ||
            elem.desc.endsWith(".xlog",false)
        ){
            //日志文件
            mSyncLogFileList.add(elem)
        }else if (
            elem.desc.endsWith(".pdf",false) ||
            elem.desc.endsWith(".doc",false) ||
            elem.desc.endsWith(".pptx",false) ||
            elem.desc.endsWith(".txt",false) ||
            elem.desc.endsWith(".docx",false) ||
            elem.desc.endsWith(".doc",false) ||
            elem.desc.endsWith(".xls",false) ||
            elem.desc.endsWith(".xlsx",false) ||
            elem.desc.endsWith(".csv",false)
        ){
            //文档文件
            mSyncTextFileList.add(elem)
        }else if (
            elem.desc.endsWith(".zip",false) ||
            elem.desc.endsWith(".rar",false) ||
            elem.desc.endsWith(".7z",false) ||
            elem.desc.endsWith(".tar",false) ||
            elem.desc.endsWith(".gz ",false) ||
            elem.desc.endsWith(".xz ",false)
        ){
            //压缩包
            mSyncZipFileList.add(elem)
        }else{
            return
        }
        progressCallback.invoke(mSyncRootList.size,count,elem)
    }

    /**
     * 清楚寻找的文件
     */
    fun clearFilesByEndWidthCache() {
        mFilesByEndWidthList.clear()
    }

    fun showDownMission() {
        try {
            mExecutorService.shutdownNow()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 读取目录下的文件内容，包含文件和文件夹
     * @param showHideFile 是否显示隐藏文件
     */
    fun getFilesAndDirectoryList(
        path: String = DEFAULT_PATH,
        showHideFile: Boolean = false
    ): MutableList<LayoutElementParcelable> {
        val filesList = mutableListOf<LayoutElementParcelable>()
        ListFilesCommand.listFiles(path, false, showHideFile,
            { mode ->

            }
        ) {
            val elem: LayoutElementParcelable? = createListParcelables(it);
            if (elem != null) {
                filesList.add(elem)
            }
        }
        return filesList
    }


    private fun createListParcelables(baseFile: HybridFileParcelable): LayoutElementParcelable? {
        if (dataUtils.isFileHidden(baseFile.path)) {
            return null
        }
        var size: String? = ""
        var longSize: Long = 0
        if (baseFile.isDirectory) {
            //mCount++
        } else {
            if (baseFile.size !== -1L) {
                try {
                    longSize = baseFile.size
                    size = Formatter.formatFileSize(Applications.context(), longSize)
                } catch (e: java.lang.NumberFormatException) {

                }
            }
        }
        return LayoutElementParcelable(
            Applications.context(),
            baseFile.getName(Applications.context()),
            baseFile.path,
            baseFile.permission,
            baseFile.link,
            size,
            longSize,
            false, baseFile.date.toString() + "",
            baseFile.isDirectory,
            true,
            baseFile.mode
        )
    }
}